/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_CONTAINER_PAGE_MGR_HPP
#define	_CUBEFS_CORE_CONTAINER_PAGE_MGR_HPP

#include <cubefs/core/container/names.hpp>
#include <cubefs/core/container/type_locator.hpp>

#include <cubefs/api/models/manager.hpp>

#define CUBEFS_SOURCE "cubefs/core/container/page_mgr.hpp:" CUBEFS_AT

namespace cubefs    {
namespace ctr       {


template <typename ID>
struct RootMapInterface: public RootMapInterfaceBase {
	virtual void set_root(const ID& id) 							= 0;
	virtual void remove_by_key(BigInt name)							= 0;
	virtual void set_value_for_key(BigInt name, const ID& page_id)	= 0;

	virtual ~RootMapInterface() {}
};

template <typename Profile, typename PageType, typename TxnType, typename Base = EmptyType>
class BasicPageManager: public Base {

	Manager* manager_;
	cubefs::api::Page* page_wrapper_;
	TypedIDValueImpl idValue;

	typedef RootMapInterface<typename PageType::ID> MyRootMapInterface;
	MyRootMapInterface* roots_;

	Container *container_;

public:

    typedef PageType            Page;
    typedef TxnType             Transaction;
    typedef typename Page::ID   ID;

    static const int            PAGE_SIZE = 4096;

    BasicPageManager(Container *container, MyRootMapInterface* roots):
    	manager_(NULL), page_wrapper_(NULL), container_(container), roots_(roots)
    {}

    MyRootMapInterface* roots() {
    	return roots_;
    }

    virtual ~BasicPageManager() throw ()
    {
    	if (page_wrapper_ != NULL)
    	{
    		delete page_wrapper_;
    	}
    }

    void set_manager(cubefs::api::Manager *manager)
    {
    	if (page_wrapper_ != NULL)
    	{
    		delete page_wrapper_;
    	}

    	manager_ = manager;
    	page_wrapper_ = manager->CreatePageWrapper();

    	manager->GetRootPageId(&idValue);

    	ID id;

    	roots_->set_root(id);
    }

    cubefs::api::Manager* get_manager() const {
    	return manager_;
    }

    Page* for_update(Page *page) {
    	return page;
    }

    Page* create_new() {
    	if (manager_ != NULL)
    	{
    		manager_->CreateNewPage(Manager::NONE, page_wrapper_);
    		return static_cast<Page*>(page_wrapper_->Ptr());
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "No page manager specified");
    	}
    }

    void free(const ID &id)
    {
    	if (manager_ != NULL)
    	{
    		idValue.SetValue(IDValue(&id));
    		manager_->RemovePage(&idValue);
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "No page manager specified");
    	}
    }

    void free(Page *page)
    {
    	if (page != NULL)
    	{
    		free(page->id());
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "Page must not be null");
    	}
    }

    Page* get(const ID &page_id)
	{
    	if (manager_ != NULL)
    	{
    		idValue.SetValue(IDValue(&page_id));
    		manager_->GetPage(page_wrapper_, &idValue);
    		return static_cast<Page*>(page_wrapper_->Ptr());
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "No page manager specified");
    	}
    }

    void new_root(BigInt name, const ID &page_id)
    {
    	if (name == 0)
    	{
    		idValue.SetValue(IDValue(&page_id));
    		manager_->SetRootPageId(&idValue);
    	}
    	else if (page_id.is_null())
    	{
    		roots_->remove_by_key(name);
    	}
    	else {
    		roots_->set_value_for_key(name, page_id);
    	}
    }

    cubefs::api::Logger& logger() {
    	if (manager_ != NULL)
    	{
    		return *manager_->GetLogger();
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "No page manager specified");
    	}
    }
};

}
}

#undef CUBEFS_SOURCE
#endif
